/*
 * mysqlObject.cpp
 *
 *  Created on: 05.04.2009
 *      Author: Christian Stupp
 */

#include "../include/mysqlInstance.h"

oal::mysqlInstance* oal::mysqlInstance::instance = 0;
MYSQL* oal::mysqlInstance::currentConnection = 0;
std::map<std::string, oal::array > oal::mysqlInstance::tableData;

/**
 * Create a new mysqlInstance over the function makeInstance.
 * This function is private
 */
oal::mysqlInstance::mysqlInstance() {
	this->error = "";
	this->currentObjectPointer = NULL;
}

/**
 * Removes all elements from the mysql object
 */
oal::mysqlInstance::~mysqlInstance() {
	for(int i=0;i<(int)this->currentItems.size();++i){
		if(this->currentItems[i] != 0) {
			delete this->currentItems[i];
			this->currentItems[i] = 0;
		}
	}
	this->currentItems.clear();
	this->items.clear();
	oal::mysqlInstance::tableData.clear();
	instance = 0;
}

/**
 * Gets the error of the last query
 *
 * @return	string	error if occure
 */
std::string oal::mysqlInstance::getError(void) {
	return this->error;
}

/**
 * Real Connect to the mysql database
 *
 * @param	string	hostname
 * @param	string	username
 * @param	string	password
 * @param	string	database
 * @param	int		port
 * @param	string	socket
 */
bool oal::mysqlInstance::connect(std::string host,string username,string password, string database, int port, string socket) {
	oal::mysqlInstance::currentConnection = mysql_init(NULL);
	const char *newSocket = NULL;
	if(socket.length() > 0) {
		newSocket = socket.c_str();
	}

	if(host.empty()) {
		cerr << "Host not set" << endl;
		return false;
	}

	if (!mysql_real_connect(
			oal::mysqlInstance::currentConnection,
			host.c_str(),
			username.c_str(),
			password.c_str(),
			database.c_str(),
			port,
			newSocket,
			0)
	) {
		debug->add("Error MySQL Connection to " + this->configuration["mysqlhost"], __FILE__, __LINE__,6);
		this->error = mysql_error(oal::mysqlInstance::currentConnection);
		return false;
	}
	this->database = database;
	mysql_select_db(
			oal::mysqlInstance::currentConnection,
			database.c_str()
	);
	this->currentObjectPointer = oal::mysqlInstance::currentConnection;
	return true;
}

/**
 * returns an sigelton instance to the mysql connection
 *
 * @return	mysqlInstance*	pointer to current object as
 * 							an singleton
 */
oal::mysqlInstance* oal::mysqlInstance::makeInstance(std::string path) {
	if(!oal::mysqlInstance::instance) {
		oal::mysqlInstance::instance = new mysqlInstance;
	}
	return oal::mysqlInstance::instance;
}

/**
 * Sets the instance of the current mysql object
 *
 * @param	*mysqlInstance	mysql object to set up
 */
void oal::mysqlInstance::setInstance(mysqlInstance *newObject) {
	instance = newObject;
	currentConnection = newObject->currentObjectPointer;
}

/**
 * Erase the current instance and close the connection to the database
 */
void oal::mysqlInstance::eraseInstance(void) {
	if(oal::mysqlInstance::instance) {
		mysql_close(currentConnection);
	    mysql_library_end();
		delete oal::mysqlInstance::instance;
		oal::mysqlInstance::instance = 0;
	}
}

/**
 * Gets the name of the current database
 *
 * @return	string	database
 */
std::string oal::mysqlInstance::getDatabase(void) {
	return this->database;
}

/**
 * Gets the fields from the given table and put it in a cache to
 * get faster the result without database connect
 *
 * @param	string	tablename to get fields
 *
 * @return	map<string,string>	fields from the table
 */
oal::array oal::mysqlInstance::getFieldsFromTable(std::string tablename) {
oal::array resultData;
MYSQL_RES *result;
MYSQL_ROW row;
MYSQL_FIELD *fields = 0;
if(!tableData[tablename].isEmpty()) {
	resultData = tableData[tablename];
} else {
	mysql_query(oal::mysqlInstance::currentConnection, ("SHOW COLUMNS FROM " + tablename).c_str());
	if (!(result = mysql_store_result(oal::mysqlInstance::currentConnection))) {
		this->error = mysql_error(oal::mysqlInstance::currentConnection);
	} else {
		int fieldsCount = 0;
		if ((fieldsCount = mysql_num_fields(result)) > 0) {
			fields = mysql_fetch_fields(result);
			for(int i = 0; i < fieldsCount; i++) {
				if(fields[i].type == PRI_KEY_FLAG) {
//					cout << fields[i].name << endl;
				}
				while((row = mysql_fetch_row(result))!= NULL) {
/*					cout << row[0] << endl;
					cout << row[1] << endl;
					cout << row[2] << endl;
					cout << row[3] << endl;*/
					resultData[row[0]] = row[3];
				}
				// cout << fields[i].name << endl;
			}
		}
	}
	tableData[tablename] = resultData;
}
	return resultData;
}

/**
 * Send an query to the mysql database and store the result in this iterator
 *
 * @param	string	query for mysql
 *
 * @return	integer	number of found records
 */
int oal::mysqlInstance::query(std::string query) {
	MYSQL_RES *result;
	MYSQL_ROW row;
	MYSQL_FIELD *fields = 0;
	this->items.clear();

	int rowsFound = 0;
	mysql_query(oal::mysqlInstance::currentConnection, query.c_str());
	if (!(result = mysql_store_result(oal::mysqlInstance::currentConnection))) {
		this->error = mysql_error(oal::mysqlInstance::currentConnection);
		if(!this->error.empty()) {
			return(-1);
		} else {
			return(mysql_affected_rows(oal::mysqlInstance::currentConnection));
		}
	} else {
		if(mysql_num_rows(result) > 0) {

			int fieldsCount = 0;
			if ((fieldsCount = mysql_num_fields(result)) > 0) {
				fields = mysql_fetch_fields(result);
				if(fields[0].type == PRI_KEY_FLAG) {
					cout << fields[0].name << endl;
				}
			}

			while((row = mysql_fetch_row(result))!= NULL) {
				object *newObject = new object();

				for(int i = 0; i < fieldsCount; i++) {
					if(row[i]) {
						newObject->set(
							fields[i].name,
							row[i]
						);
					}
				}
				rowsFound++;

				this->append(newObject);
				this->currentItems.push_back(newObject);
			}
		}
	}
	this->currentPosition = 0;
	return rowsFound;
}

/**
 * Fetchs the data returns from mysql_query as assoc keys in a map
 *
 * @return	std::map<string,string>	data from mysql
 */
oal::object *oal::mysqlInstance::mysqlFetchAssoc(void) {
	object *result = 0;
	if(currentPosition < (int)this->items.size()) {
		result = this->items[currentPosition];
		this->currentPosition++;
	}
	return result;
}

string oal::mysqlInstance::buildQueryStringFromMap(map<string,string> value) {
	string result="";
	std::map<std::string,std::string>::iterator iterator;
	for(
			iterator = value.begin();
			iterator != value.end();
			iterator++
	) {
		result += iterator->first + "=\"" + iterator->second + "\",";
	}
	if(!result.empty()) {
		result = result.substr(0,result.length()-1);
	}
	return result;
}

string oal::mysqlInstance::lastInsertId(void) {
	return intToStr(mysql_insert_id(oal::mysqlInstance::currentConnection));
}

void oal::mysqlInstance::freeResult(void) {
}


////////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////////

int oal::mysql_query(std::string query) {
	return oal::mysqlInstance::makeInstance()->query(query);
}

bool oal::mysql_connect(std::string host,string username,string password, string database, int port, string socket) {
	return oal::mysqlInstance::makeInstance()->connect(host,username,password,database,port,socket);
}

oal::array oal::mysql_fetch_fields(std::string table) {
	return oal::mysqlInstance::makeInstance()->getFieldsFromTable(table);
}
oal::object* oal::mysql_fetch_assoc(void) {
	return oal::mysqlInstance::makeInstance()->mysqlFetchAssoc();
}

std::string oal::mysql_error(void) {
	return oal::mysqlInstance::makeInstance()->getError();
}

void oal::mysql_close(void) {
	oal::mysqlInstance::eraseInstance();
}

void oal::mysql_free_result(void) {
	oal::mysqlInstance::makeInstance()->freeResult();
}
// string mysql_insert_id() {
//	return oal::mysqlInstance::makeInstance()->lastInsertId();
// }

/**
 * Inserts an record into the mysql database
 *
 * @param	string	tablename to insert
 * @param	map		map with string key and string value
 * 					with the content
 * @return	string	last insert id if successful saved,
 * 					0 otherwise
 */
string oal::mysql_insert_query(std::string table, std::map<std::string,std::string> value) {
	oal::mysqlInstance *currentSql = oal::mysqlInstance::makeInstance();
	currentSql->query("INSERT INTO " + table + " SET " +
			currentSql->buildQueryStringFromMap(value) + ""
	);
	return currentSql->lastInsertId();
}

/**
 * Updates an record into the mysql database. the key uid must be set
 *
 * @param	string	tablename to update
 * @param	map		map with string key and string value
 * 					with the content
 */
void oal::mysql_update_query(std::string table,std::string where, std::map<std::string,std::string> value) {
	oal::mysqlInstance *currentSql = oal::mysqlInstance::makeInstance();
	currentSql->query(
		"UPDATE " + table + " SET " +
		currentSql->buildQueryStringFromMap(value) + " " +
		where + ";"
	);
}
